import java.util.NoSuchElementException;
import java.util.concurrent.Semaphore;

/**
 * STS.java
 * Short Term Scheduler
 * @author Will
 * Notes: Must be made thread Safe using lock/semaphore. 
 */
public class STS extends Thread{
	public static Object semaphore = new Object();
	public static Semaphore s = new Semaphore(1, true);
	public static CPU[] cpus = new CPU[Driver.NUMBER_OF_CPUs];
	
	public static void Initialize(){
		synchronized(semaphore){
			for(int i = 0; i < cpus.length; i++){
				cpus[i] = new CPU();							//Add new CPU to array.
				cpus[i].ID = i;
				cpus[i].process = Jobs.ready.pop();				//Give it the next process in the Ready Queue
				cpus[i].process.state = Process.State.RUNNING;	//Set Process State to Running
				cpus[i].process.setTime(true, false);			//Set the Waiting End Time
				cpus[i].process.setTime(false, false);			//Set the executing start time.
				//cpus[i].start();
			}
			for(CPU c: cpus)
				c.start();
		}
	}
	
	public void run(){
		
	}
	
	public static boolean Working(){
		for(CPU c: cpus){
			if(c.isAlive())
				return true;						
		}
		return false;
	}
	
	/**
	 * Called by a CPU to notify that STS that it has completed 
	 * the execution of a Process. 
	 * @param p The Process that has Just Finished Execution
	 */
	public static Process Finished(Process p){		
		Process next;
		try {
			p.setTime(false, false); 					// Stop the Execution Timer for the Process;
			s.acquire();			 					// ENTERING A CRITICAL SECTION
			if(p.size > 0){
				MemoryManager.Remove(p); 					// Returns Occupied Memory to RAM, Sets Process State to TERMINATED
				BookKeeper.addWait(p.getTime(true));		// Add the accounting info 
				BookKeeper.addExec(p.getTime(false));		// To the Book Keeper
				BookKeeper.addInstructions(p.length);
				BookKeeper.addIO(p.numIO);
			}
			if(Jobs.ready.size() <= 0 && Jobs.waiting.size() > 0){	//See if we have any jobs waiting
				LTS.schedule();
			}
			if(Jobs.ready.size() <= 0 && Jobs.waiting.size() > 0){ 	//If we still have no ready jobs then we have an issue
				s.release();										//where we have to wait for some jobs to finish to
				System.out.println("Sending a blank process");
				next = new Process(0,0,0,0,0,0,0,0);				//Add this process. So we send a process to tell the
				return next;
			}														//CPU to wait.
			else
				next = Jobs.ready.pop();			// Get the Next Process			
			next.state = Process.State.RUNNING;
			s.release();			 					// LEAVING CRITICAL SECTION
			next.setTime(true, false); 					//Sets waiting end time.
			next.setTime(false, true); 					//Set executing start time.
			return next;
		} 
		catch (InterruptedException e) {
			s.release();
			e.printStackTrace();
			return null;
		}
		catch(NoSuchElementException nse){
			s.release();			
			return null;
		}
	}
}
